package com.wgz.medicalmanagement.service.impl;

import com.wgz.medicalmanagement.domain.entity.User;
import com.wgz.medicalmanagement.domain.dto.LoginRequest;
import com.wgz.medicalmanagement.domain.dto.RegisterRequest;
import com.wgz.medicalmanagement.domain.vo.LoginResponse;
import com.wgz.medicalmanagement.domain.vo.UserInfoVO;
import com.wgz.medicalmanagement.infrastructure.security.JwtUtils;
import com.wgz.medicalmanagement.repository.UserRepository;
import com.wgz.medicalmanagement.service.UserService;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.DisabledException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * 用户服务实现类
 *
 * @author medical
 * @since 2023-05-29
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class UserServiceImpl implements UserService {

    private final UserRepository userRepository;
    private final PasswordEncoder passwordEncoder;
    private final RedisTemplate<String, Object> redisTemplate;
    private final HttpServletRequest request;
    private final JwtUtils jwtUtils;
    
    @Value("${app.security.jwt.token-prefix}")
    private String tokenPrefix;

    /**
     * 用户登录
     *
     * @param loginRequest 登录请求
     * @return 登录响应
     */
    @Override
    public LoginResponse login(LoginRequest loginRequest) {
        try {
            // 直接从数据库查询用户
            User user = userRepository.findByUsername(loginRequest.getUsername());
            
            // 验证用户是否存在
            if (user == null) {
                log.error("用户名不存在: {}", loginRequest.getUsername());
                throw new BadCredentialsException("用户名或密码不正确");
            }
            
            // 验证用户是否启用
            if (!user.getEnabled()) {
                log.error("用户已被禁用: {}", loginRequest.getUsername());
                throw new DisabledException("用户已被禁用");
            }
            
            // 验证密码
            if (!passwordEncoder.matches(loginRequest.getPassword(), user.getPassword())) {
                log.error("密码不正确: {}", loginRequest.getUsername());
                throw new BadCredentialsException("用户名或密码不正确");
            }
            
            // 创建用户详情
            List<SimpleGrantedAuthority> authorities = new ArrayList<>();
            authorities.add(new SimpleGrantedAuthority("ROLE_USER"));
            UserDetails userDetails = new org.springframework.security.core.userdetails.User(
                user.getUsername(), 
                user.getPassword(), 
                user.getEnabled(),
                user.getAccountNonExpired(),
                user.getCredentialsNonExpired(),
                user.getAccountNonLocked(),
                authorities
            );
            
            // 创建认证对象并设置到安全上下文
            UsernamePasswordAuthenticationToken authentication = 
                new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
            SecurityContextHolder.getContext().setAuthentication(authentication);
            
            // 生成Token，考虑"记住我"选项
            boolean rememberMe = loginRequest.getRememberMe() != null && loginRequest.getRememberMe();
            String token = jwtUtils.generateToken(userDetails, rememberMe);
            
            try {
                // 设置较短的超时时间，避免Redis连接问题导致登录过慢
                String redisKey = "token:" + userDetails.getUsername();
                // 使用异步方式存储Token到Redis
                long expirationTime = jwtUtils.getTokenExpirationMillis(rememberMe);
                redisTemplate.opsForValue().set(redisKey, token, expirationTime, TimeUnit.MILLISECONDS);
                log.debug("令牌已存入Redis: {}", redisKey);
            } catch (Exception e) {
                // Redis连接失败时不阻断流程
                log.warn("无法连接到Redis，继续使用本地认证模式: {}", e.getMessage());
            }
            
            // 返回登录信息
            return LoginResponse.builder()
                    .token(token)
                    .tokenType(tokenPrefix.trim())
                    .userId(user.getId())
                    .username(user.getUsername())
                    .realName(user.getName())
                    .avatar("")
                    .expiresIn(new Date(System.currentTimeMillis() + 
                              (rememberMe ? jwtUtils.getTokenExpirationMillis(true) : jwtUtils.getTokenExpirationMillis(false))))
                    .build();
        } catch (DisabledException e) {
            log.error("用户已被禁用: {}", loginRequest.getUsername());
            throw new RuntimeException("用户已被禁用");
        } catch (BadCredentialsException e) {
            log.error("用户名或密码不正确: {}", loginRequest.getUsername());
            throw new RuntimeException("用户名或密码不正确");
        } catch (Exception e) {
            log.error("登录过程中发生错误: {}", e.getMessage());
            throw new RuntimeException("登录失败: " + e.getMessage());
        }
    }
    
    /**
     * 用户注册
     *
     * @param registerRequest 注册请求
     * @return 注册后的用户对象
     */
    @Override
    @Transactional
    public User register(RegisterRequest registerRequest) {
        // 防重复提交变量
        String lockKey = "register_lock:" + registerRequest.getUsername();
        Boolean locked = false;
        boolean redisAvailable = true;
        // 首先检查用户名是否已存在 - 这部分放到锁之外，可以快速失败
        if (existsByUsername(registerRequest.getUsername())) {
            log.error("注册失败：用户名 {} 已存在", registerRequest.getUsername());
            throw new RuntimeException("用户名已存在");
        }
        try {
            // 尝试获取分布式锁，防止重复注册
            try {
                // 设置更长的锁超时时间，防止在高负载情况下锁过早释放
                locked = redisTemplate.opsForValue().setIfAbsent(lockKey, "1", 30, TimeUnit.SECONDS);
                if (locked == null || !locked) {
                    log.warn("注册失败：用户 {} 注册操作正在处理中，请不要重复提交", registerRequest.getUsername());
                    throw new RuntimeException("注册操作正在处理中，请不要重复提交");
                }
            } catch (Exception e) {
                // Redis连接失败，记录警告但继续注册流程
                log.warn("Redis连接失败，无法使用分布式锁防止重复提交: {}", e.getMessage());
                redisAvailable = false;
                // 继续执行注册流程，但没有分布式锁保护
            }
            // 再次检查用户名是否已存在（双重检查，避免在获取锁过程中用户被创建）
            if (existsByUsername(registerRequest.getUsername())) {
                log.error("注册失败：用户名 {} 已存在", registerRequest.getUsername());
                throw new RuntimeException("用户名已存在");
            }
            // 验证密码是否一致
            if (registerRequest.getConfirmPassword() != null && 
                !registerRequest.getPassword().equals(registerRequest.getConfirmPassword())) {
                log.error("注册失败：两次输入的密码不一致");
                throw new RuntimeException("两次输入的密码不一致");
            }
            // 创建新用户
            User user = new User();
            user.setUsername(registerRequest.getUsername());
            user.setPassword(passwordEncoder.encode(registerRequest.getPassword()));
            user.setName(registerRequest.getRealName());
            user.setEmail(registerRequest.getEmail());
            user.setRole(registerRequest.getRole());
            // 处理手机号码
            if (registerRequest.getPhoneNumber() != null && !registerRequest.getPhoneNumber().isEmpty()) {
                user.setPhone(registerRequest.getPhoneNumber());
            } else if (registerRequest.getMobile() != null && !registerRequest.getMobile().isEmpty()) {
                user.setPhone(registerRequest.getMobile());
            }
            // 设置账户状态
            user.setEnabled(true);
            user.setAccountNonExpired(true);
            user.setAccountNonLocked(true);
            user.setCredentialsNonExpired(true);
            // 设置创建时间
            user.setCreatedTime(LocalDateTime.now());
            
            // 保存用户
            User savedUser = userRepository.save(user);
            log.info("用户注册成功: {}", savedUser.getUsername());
            
            return savedUser;
        } finally {
            // 释放分布式锁
            if (redisAvailable && locked != null && locked) {
                try {
                    redisTemplate.delete(lockKey);
                    log.debug("注册锁已释放: {}", lockKey);
                } catch (Exception e) {
                    log.warn("释放注册锁失败: {}", e.getMessage());
                }
            }
        }
    }
    
    /**
     * 检查用户名是否已存在
     *
     * @param username 用户名
     * @return 如果用户名已存在则返回true，否则返回false
     */
    @Override
    public boolean existsByUsername(String username) {
        return userRepository.existsByUsername(username);
    }
    
    /**
     * 获取当前登录用户信息
     *
     * @return 用户信息
     */
    @Override
    public UserInfoVO getCurrentUserInfo() {
        // 获取当前认证的用户名
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        String username = authentication.getName();
        
        // 获取用户信息
        User user = getUserByUsername(username);
        if (user == null) {
            throw new UsernameNotFoundException("未找到用户: " + username);
        }
        
        // 获取角色和权限
        List<String> roles = new ArrayList<>();
        List<String> permissions = new ArrayList<>();
        
        // TODO: 从数据库获取用户角色和权限
        roles.add("ROLE_USER");
        
        // 构建用户信息
        return UserInfoVO.builder()
                .userId(user.getId())
                .username(user.getUsername())
                .realName(user.getName())
                .email(user.getEmail())
                .mobile(user.getPhone())
                .avatar("")
                .gender(null)
                .status(user.getEnabled() ? 1 : 0)
                .roles(roles)
                .permissions(permissions)
                .build();
    }

    /**
     * 根据用户名获取用户
     *
     * @param username 用户名
     * @return 用户对象
     */
    @Override
    public User getUserByUsername(String username) {
        return userRepository.findByUsername(username);
    }

    /**
     * 根据ID获取用户
     *
     * @param userId 用户ID
     * @return 用户对象
     */
    @Override
    public User getUserById(Long userId) {
        return userRepository.findById(userId).orElse(null);
    }
    
    /**
     * 注销登录
     */
    @Override
    public void logout() {
        try {
            // 获取当前用户信息
            Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
            if (authentication != null && authentication.isAuthenticated()) {
                String username = authentication.getName();
                
                // 从Redis中删除令牌
                try {
                    String redisKey = "token:" + username;
                    redisTemplate.delete(redisKey);
                    log.debug("用户令牌已从Redis中删除: {}", redisKey);
                } catch (Exception e) {
                    log.warn("从Redis中删除令牌失败: {}", e.getMessage());
                }
                
                // 清除安全上下文
                SecurityContextHolder.clearContext();
                log.info("用户 {} 已成功注销", username);
            }
        } catch (Exception e) {
            log.error("注销过程中发生错误: {}", e.getMessage());
        }
    }
} 